package org.univie.mappr;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;

import org.jdom.DataConversionException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import android.util.Log;
import android.preference.EditTextPreference;

import com.google.android.maps.GeoPoint;



/**
 * This class retrieves photos from Flickr by querying with a location or the ID
 */
public abstract class FlickrClient {
	private static final String TAG = "org.univie.mappr.FlickrClient";
	
	private static final String API_KEY = "b486b4df116dc7c19ffde499c62223ba";
	private static final String FLICKR_HOST = "http://www.flickr.com/services/rest/?";
	private static final int MAX_RESULTS = MapprView.sMaxResults;
	

	public static final int FLICKR_ACCURACY_WORLD = 1;
	public static final int FLICKR_ACCURACY_COUNTRY = 3;
	public static final int FLICKR_ACCURACY_REGION = 6;
	public static final int FLICKR_ACCURACY_CITY = 11;
	public static final int FLICKR_ACCURACY_STREET = 16;
		
	private static final double BBOX_PERCENT = 0.05;

	/**
	 * Returns a Vector of {@link FlickPhoto}s that belong to the given location
	 * under a certain (optional) accuracy
	 * 
	 * @param location The current location
	 * @return A Vector of {@link FlickPhoto}s
	 */
	public static Vector<FlickrPhoto> getPhotosForLocation(GeoPoint location,int Laspan,int Lospan) {
		//TODO: integrate Laspan and Lospan -> should be latitude +/- Laspan/2 for min/max boxes
		
		// more info :
		// http://www.flickr.com/services/api/flickr.photos.search.html

		Log.d(TAG, "getPhotosForLocation called with location: " + location.toString());

		// perfect would be the minlat/lon the top left corner of the display
		// and max... the bottom right corner
		
		
		String minlat = String.valueOf((location.getLatitudeE6()-Laspan/2) / 1E6);
		String minlon = String.valueOf((location.getLongitudeE6()-Lospan/2) / 1E6);
		String maxlon = String.valueOf((location.getLongitudeE6()+Laspan/2) / 1E6);
		String maxlat = String.valueOf((location.getLatitudeE6() +Lospan/2) / 1E6);

		String BBox = minlon + "," + minlat + "," + maxlon + "," + maxlat;
		
		Log.d(TAG, "BBox created: " + BBox);
		
		/*
		 * privacy_filter values:
		 * 
		 * 1 public photos 
		 * 2 private photos visible to friends 
		 * 3 private photos visible to family 
		 * 4 private photos visible to friends & family 
		 * 5 completely private photos
		 */
		String privacy_filter = "1";

		/*
		 * Content Type setting:
		 * 
		 * 1 for photos only. 
		 * 2 for screenshots only. 
		 * 3 for 'other' only. 
		 * 4 for photos and screenshots. 
		 * 5 for screenshots and 'other'. 
		 * 6 for photos and 'other'. 
		 * 7 for photos, screenshots, and 'other' (all).
		 */
		String content_type = "7";


		// Filter results by media type. Possible values are all (default),
		// photos or videos
		String media = "photos";

		/*
		 * sort (Optional)
		 * 
		 * The order in which to sort returned photos. Deafults to
		 * date-posted-desc (unless you are doing a radial geo query, in which
		 * case the default sorting is by ascending distance from the point
		 * specified). 
		 * The possible values are: date-posted-asc,
		 * date-posted-desc, date-taken-asc, date-taken-desc,
		 * interestingness-desc, interestingness-asc, and relevance.
		 * 
		 */
		String sort = "interestingness-asc";


		// make HTTP request
		URI FindPhotosReq = null;
		
		try {
			FindPhotosReq = new URI(FLICKR_HOST
					+ "method=flickr.photos.search&api_key=" + API_KEY
					+ "&sort=" + sort + "&privacy_filter=" + privacy_filter
					+ "&bbox=" + BBox + "&content_type=" + content_type
					+ "&media=" + media + "&per_page=" + MAX_RESULTS);
			Log.d(TAG, "Sending request: " + FindPhotosReq.toString());
		} catch (URISyntaxException e) {
			Log.e(TAG,"URISyntaxException: "+e.toString());

		}

		// get the response
		SAXBuilder builder = new SAXBuilder();
		Document doc = null;
		XMLOutputter xmlout = new XMLOutputter();
		
		Vector<FlickrPhoto> result = new Vector<FlickrPhoto>();
		try {
			doc = builder.build(FindPhotosReq.toURL());
			Element root = doc.getRootElement();
			
			//If no photos there try search with places
			if(root.getAttributeValue("total")=="0"){
				Log.d(TAG, "no photos in this bbox, trying with places xml tree:\n"+xmlout.outputString(doc));
				// getting place id could be used instead of bbox
				//TODO: better for accuracy than 7
				String[] placeandwoeid = getPlaceId(location, 7);
				FindPhotosReq = new URI(FLICKR_HOST
						+ "method=flickr.photos.search&api_key=" + API_KEY
						+ "&sort=" + sort + "&privacy_filter=" + privacy_filter
						+ "&content_type=" + content_type 
						+ "&woe_id=" + placeandwoeid[1]
						+ "&place_id=" + placeandwoeid[0]
						+ "&media=" + media + "&per_page=" + MAX_RESULTS);
				doc = builder.build(FindPhotosReq.toURL());
				root = doc.getRootElement();
				//if there also no photos than return the blank result
				if(root.getAttributeValue("total")=="0"){
					Log.d(TAG, "no photos in this place xml tree:\n"+xmlout.outputString(doc));
					return result;	
				}
			}
			result = getPhotosFromXml(xmlout.outputString(doc));
		} catch (MalformedURLException e) {
			Log.e(TAG,"MalformedURLException: "+e.toString());

		} catch (JDOMException e) {
			Log.e(TAG,"JDOMException: "+e.toString());

		} catch (IOException e) {
			Log.e(TAG,"IOException: "+e.toString());
		}
		catch (URISyntaxException e) {
			Log.e(TAG,"URISyntaxException: "+e.toString());
		}

		return result;
	}

	
	
	/**
	 * Returns a Vector of FlickrPhotoIds that belong to the given location
	 * under a certain (optional) accuracy
	 * 
	 * @param location The current location
	 * @return A Vector of {@link FlickPhoto}s
	 */
	
	public static Vector<String> getPhotoIdsForLocation(GeoPoint location,int Laspan,int Lospan) {
		//TODO: integrate Laspan and Lospan -> should be latitude +/- Laspan/2 for min/max boxes
		
		// more info :
		// http://www.flickr.com/services/api/flickr.photos.search.html

		Log.d(TAG, "getPhotosForLocation called with location: " + location.toString());

		// perfect would be the minlat/lon the top left corner of the display
		// and max... the bottom right corner
		
		
		String minlat = String.valueOf((location.getLatitudeE6()-Laspan/2) / 1E6);
		String minlon = String.valueOf((location.getLongitudeE6()-Lospan/2) / 1E6);
		String maxlon = String.valueOf((location.getLongitudeE6()+Laspan/2) / 1E6);
		String maxlat = String.valueOf((location.getLatitudeE6() +Lospan/2) / 1E6);

		String BBox = minlon + "," + minlat + "," + maxlon + "," + maxlat;
		
		Log.d(TAG, "BBox created: " + BBox);
		
		/*
		 * privacy_filter values:
		 * 
		 * 1 public photos 
		 * 2 private photos visible to friends 
		 * 3 private photos visible to family 
		 * 4 private photos visible to friends & family 
		 * 5 completely private photos
		 */
		String privacy_filter = "1";

		/*
		 * Content Type setting:
		 * 
		 * 1 for photos only. 
		 * 2 for screenshots only. 
		 * 3 for 'other' only. 
		 * 4 for photos and screenshots. 
		 * 5 for screenshots and 'other'. 
		 * 6 for photos and 'other'. 
		 * 7 for photos, screenshots, and 'other' (all).
		 */
		String content_type = "7";


		// Filter results by media type. Possible values are all (default),
		// photos or videos
		String media = "photos";

		/*
		 * sort (Optional)
		 * 
		 * The order in which to sort returned photos. Deafults to
		 * date-posted-desc (unless you are doing a radial geo query, in which
		 * case the default sorting is by ascending distance from the point
		 * specified). 
		 * The possible values are: date-posted-asc,
		 * date-posted-desc, date-taken-asc, date-taken-desc,
		 * interestingness-desc, interestingness-asc, and relevance.
		 * 
		 */
		String sort = "interestingness-asc";


		// make HTTP request
		URI FindPhotosReq = null;
		
		try {
			FindPhotosReq = new URI(FLICKR_HOST
					+ "method=flickr.photos.search&api_key=" + API_KEY
					+ "&sort=" + sort + "&privacy_filter=" + privacy_filter
					+ "&bbox=" + BBox + "&content_type=" + content_type
					+ "&media=" + media + "&per_page=" + MAX_RESULTS);
			Log.d(TAG, "Sending request: " + FindPhotosReq.toString());
		} catch (URISyntaxException e) {
			Log.e(TAG,"URISyntaxException: "+e.toString());

		}

		// get the response
		SAXBuilder builder = new SAXBuilder();
		Document doc = null;
		XMLOutputter xmlout = new XMLOutputter();
		
		Vector<String> result = new Vector<String>();
		try {
			doc = builder.build(FindPhotosReq.toURL());
			Element root = doc.getRootElement();
			
			//If no photos there try search with places
			if(root.getAttributeValue("total")=="0"){
				Log.d(TAG, "no photos in this bbox, trying with places xml tree:\n"+xmlout.outputString(doc));
				// getting place id could be used instead of bbox
				//TODO: better for accuracy than 7
				String[] placeandwoeid = getPlaceId(location, 7);
				FindPhotosReq = new URI(FLICKR_HOST
						+ "method=flickr.photos.search&api_key=" + API_KEY
						+ "&sort=" + sort + "&privacy_filter=" + privacy_filter
						+ "&content_type=" + content_type 
						+ "&woe_id=" + placeandwoeid[1]
						+ "&place_id=" + placeandwoeid[0]
						+ "&media=" + media + "&per_page=" + MAX_RESULTS);
				doc = builder.build(FindPhotosReq.toURL());
				root = doc.getRootElement();
				//if there also no photos than return the blank result
				if(root.getAttributeValue("total")=="0"){
					Log.d(TAG, "no photos in this place xml tree:\n"+xmlout.outputString(doc));
					return result;	
				}
			}
			result = getPhotoIdsFromXml(xmlout.outputString(doc));
		} catch (MalformedURLException e) {
			Log.e(TAG,"MalformedURLException: "+e.toString());

		} catch (JDOMException e) {
			Log.e(TAG,"JDOMException: "+e.toString());

		} catch (IOException e) {
			Log.e(TAG,"IOException: "+e.toString());
		}
		catch (URISyntaxException e) {
			Log.e(TAG,"URISyntaxException: "+e.toString());
		}

		return result;
	}
	
	
	
	/**
	 * Returns a Flickr photo with the given ID
	 * 
	 * @param id
	 *            The ID of the Flickr photo
	 * @return The photo that corresponds to the ID
	 */
	public static FlickrPhoto getPhotoForId(String id) {

		Log.d(TAG, "getPhotoForId called with id: " + id);

		FlickrPhoto result = null;

		String photoinfo = FLICKR_HOST
				+ "method=flickr.photos.getInfo&api_key=" + API_KEY
				+ "&photo_id=";
		
		ArrayList<String> Tags = new ArrayList<String>();

		Document doc = null;
		Log.d(TAG, "url: "+photoinfo+id);

		// if we already had an entry for the photo
		if (PhotoCache.hasEntryForId(id)) {
			Log.d(TAG, "PhotoCache.hasEntryForId -> true");
			try {
				String xmlResponse = PhotoCache.getXmlForPhoto(id); 
				SAXBuilder builder = new SAXBuilder();
				doc = builder.build(new ByteArrayInputStream(xmlResponse.getBytes()));
			} catch (JDOMException e) {
				Log.e(TAG,"JDOMException: "+e.toString());

			} catch (IOException e) {
				Log.e(TAG,"IOException: "+e.toString());

			}
		} 
		
		// if not
		else {	
			Log.d(TAG, "PhotoCache.hasEntryForId -> false");

			try {
				SAXBuilder builder = new SAXBuilder();
				doc = builder.build(new URI(photoinfo + id).toURL());
			//	XMLOutputter xmlout = new XMLOutputter();
				PhotoCache.addXmlForPhoto(id, doc);
			} catch (MalformedURLException e) {
				Log.e(TAG,"MalformedURLException"+photoinfo+id);
			} catch (JDOMException e) {
				Log.e(TAG,"JDOMException"+photoinfo+id);
			} catch (IOException e) {
				Log.e(TAG,"JDOMException"+photoinfo+id);
			} catch (URISyntaxException e) {
				Log.e(TAG,"JDOMException"+photoinfo+id);
			}
		}
		// XMLOutputter xmlout = new XMLOutputter();
		// System.out.print(xmlout.outputString(doc));
		
		/*
		 * <photo id="2733" secret="123456" server="12"
		 *		isfavorite="0" license="3" rotation="90" 
		 *		originalsecret="1bc09ce34a" originalformat="png">
		 *		<owner nsid="12037949754@N01" username="Bees"
		 *			realname="Cal Henderson" location="Bedford, UK" />
		 *		<title>orford_castle_taster</title>
		 *		<description>hello!</description>
		 *		<visibility ispublic="1" isfriend="0" isfamily="0" />
		 *		<dates posted="1100897479" taken="2004-11-19 12:51:19"
		 *			takengranularity="0" lastupdate="1093022469" />
		 *		<permissions permcomment="3" permaddmeta="2" />
		 *		<editability cancomment="1" canaddmeta="1" />
		 *		<comments>1</comments>
		 *		<notes>
		 *			<note id="313" author="12037949754@N01"
		 *				authorname="Bees" x="10" y="10"
		 *				w="50" h="50">foo</note>
		 *		</notes>
		 *		<tags>
		 *			<tag id="1234" author="12037949754@N01" raw="woo yay">wooyay</tag>
		 *			<tag id="1235" author="12037949754@N01" raw="hoopla">hoopla</tag>
		 *		</tags>
		 *		<urls>
		 *			<url type="photopage">http://www.flickr.com/photos/bees/2733/</url> 
		 *		</urls>
		 *	</photo>
		 * 
		 */
		
		Element root = doc.getRootElement();
		Element photo = root.getChild("photo");
		Element owner = photo.getChild("owner");
		Element date = photo.getChild("dates");
		Element title = photo.getChild("title");
		Element location = photo.getChild("location");
		Element desc = photo.getChild("description");
		Element tag = photo.getChild("tags");
		List<Element> etags = tag.getChildren("tag");
		

		result = new FlickrPhoto(photo.getAttributeValue("id"), Integer
				.parseInt(photo.getAttributeValue("farm")), Integer
				.parseInt(photo.getAttributeValue("server")), photo
				.getAttributeValue("secret"));


		result.setTitle(title.getTextNormalize());


		Log.d(TAG,"getAttributelongitude"+location.toString());
		double lon = Double.parseDouble(location.getAttributeValue("longitude"));
		Log.d(TAG, "getAttributelatitude"+location.getAttributeValue("latitude"));
		double lat = Double.parseDouble(location.getAttributeValue("latitude"));
		Log.d(TAG, "new GeoPoint");
		GeoPoint photolocation = new GeoPoint((int)(lat*1E6),(int)(lon*1E6));
		Log.d(TAG, "setLocation");
		result.setLocation(photolocation);
		
		Log.d(TAG,"setOwner");
		result.setOwner(owner.getAttribute("nsid").getValue(), owner
				.getAttributeValue("username"), owner
				.getAttributeValue("realname"));

		Log.d(TAG,"setDatePosted");
		result.setDatePosted(new Date(Long.parseLong(date
				.getAttributeValue("posted")))); // millisec since 1970

		Log.d(TAG,"SimpleDateFormat");
		SimpleDateFormat sdfToDate = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
		try { // YYYY-MM-DD HH:MM:SS
			result.setDateTaken(sdfToDate
					.parse(date.getAttributeValue("taken")));
		} catch (ParseException e) {
			Log.e(TAG,"ParseException: "+e.toString());

		}
		
		result.setDesc(desc.getTextNormalize());
		
		for (Element tTag : etags) {
			Tags.add(tTag.getTextNormalize());
		}
		Log.d(TAG,"setTags");
		String str [] = (String []) Tags.toArray (new String [Tags.size ()]);
		result.setTags(str);

		return result;
	}

	/**
	 * Returns the ID of a Flickr Place for a given location and accuracy
	 * @param location The current location as a {@link GeoPoint}
	 * @param accuracy The accuracy (see {@link FlickrClient})
	 * @return String[0] The ID of the Flickr Place for the location
	 * 		   String[1] The WoeId of Flickr Place for the location 
	 */
	private static String[] getPlaceId(GeoPoint location, int accuracy) {
		String[] WoeandplaceId = new String[2];
		URI placesByLatLong = null;
		try {
			placesByLatLong = new URI(FLICKR_HOST
					+ "method=flickr.places.findByLatLon&api_key=" + API_KEY
					+ "&lat=" + String.valueOf(location.getLatitudeE6())
					+ "&lon=" + String.valueOf(location.getLongitudeE6())
					+ "&accuracy=" + String.valueOf(accuracy));
		} catch (URISyntaxException e) {
			Log.e(TAG,"URISyntaxException: "+e.toString());

		}
	
		SAXBuilder builder = new SAXBuilder();
		Document doc = null;
		//XMLOutputter xmlout = new XMLOutputter();
	
		try {
			doc = builder.build(placesByLatLong.toURL());
			// System.out.print(xmlout.outputString(doc)); //DEBUG
			Element root = doc.getRootElement();
			// System.err.print(root.toString()); //DEBUG
			Element places = root.getChild("places");
			Element place = places.getChild("place");
			WoeandplaceId[0] = place.getAttributeValue("place_id");
			WoeandplaceId[1] = place.getAttributeValue("woeid");
		} catch (MalformedURLException e) {
			Log.e(TAG,"MalformedURLException: "+e.toString());
		} catch (JDOMException e) {
			Log.e(TAG,"JDOMException: "+e.toString());
		} catch (IOException e) {
			Log.e(TAG,"IOException: "+e.toString());

		}
	
		return WoeandplaceId;
	}

	/**
	 * Creates the {@link FlickPhoto} elements that are contained within an XML
	 * response from Flickr.
	 * 
	 * @param response
	 *            The XML response obtained by Flickr
	 * @return A Vector containing the photos in the response
	 */
	@SuppressWarnings("unchecked")
	private static Vector<FlickrPhoto> getPhotosFromXml(String response) {

		Log.d(TAG, "getPhotosFromXml called");

		InputStream ISresponse = new ByteArrayInputStream(response.getBytes());
		Vector<FlickrPhoto> result = new Vector<FlickrPhoto>();
		SAXBuilder builder = new SAXBuilder();
		Document doc = null;
		// System.out.print(response);//DEBUG
		try {
			doc = builder.build(ISresponse);
		} catch (JDOMException e) {
			Log.e(TAG, "JDOMException caught: " + e.toString());
		} catch (IOException e) {
			Log.e(TAG, "IOException caught: " + e.toString());
		}
			/* 	
			 * response:
			 * 	<photos page="2" pages="89" perpage="10" total="881">
			 * 		<photo id="2636" owner="47058503995@N01" 
			 *		secret="a123456" server="2" title="test_04"
			 *		ispublic="1" isfriend="0" isfamily="0" />
			 * </photos>
		     *
			 */
			
		// add details for every photo
		try {
			Element root = doc.getRootElement();
			Element photos = root.getChild("photos");
			List<Element> photo = photos.getChildren("photo");
			Log.d(TAG, "getPhotosFromXml found " + photo.size() + " photos");
			
			if (!photo.isEmpty()) {
				int psize = photo.size();
				int done = 1;
				for (Element i : photo) {
					result.add(getPhotoForId(i.getAttributeValue("id")));
					MapprView.sLoadingBar.setProgress((done++*100)/(psize));
					
				}				
			}
		} catch (Exception e) {
			Log.e(TAG, "Exception caught: " + e.toString());
		}
		return result;
	}
	
	/**
	 * Creates the {@link FlickPhoto} elements that are contained within an XML
	 * response from Flickr.
	 * 
	 * @param response
	 *            The XML response obtained by Flickr
	 * @return A Vector containing the photos in the response
	 */
	@SuppressWarnings("unchecked")
	private static Vector<String> getPhotoIdsFromXml(String response) {

		Log.d(TAG, "getPhotosFromXml called");

		InputStream ISresponse = new ByteArrayInputStream(response.getBytes());
		Vector<String> result = new Vector<String>();
		SAXBuilder builder = new SAXBuilder();
		Document doc = null;
		// System.out.print(response);//DEBUG
		try {
			doc = builder.build(ISresponse);
		} catch (JDOMException e) {
			Log.e(TAG, "JDOMException caught: " + e.toString());
		} catch (IOException e) {
			Log.e(TAG, "IOException caught: " + e.toString());
		}
			/* 	
			 * response:
			 * 	<photos page="2" pages="89" perpage="10" total="881">
			 * 		<photo id="2636" owner="47058503995@N01" 
			 *		secret="a123456" server="2" title="test_04"
			 *		ispublic="1" isfriend="0" isfamily="0" />
			 * </photos>
		     *
			 */
			
		// add details for every photo
		try {
			Element root = doc.getRootElement();
			Element photos = root.getChild("photos");
			List<Element> photo = photos.getChildren("photo");
			Log.d(TAG, "getPhotosFromXml found " + photo.size() + " photos");
			//getting all ids
			if (!photo.isEmpty()) {
				int psize = photo.size();
				int done = 1;
				for (Element i : photo) {
					result.add(i.getAttributeValue("id"));
				//	MapprView.sLoadingBar.setProgress((done++*100)/(psize));	
				}				
			}
		} catch (Exception e) {
			Log.e(TAG, "Exception caught: " + e.toString());
		}
		return result;
	}
	
}
